<!doctype html>
<html lang="en">
	<head>
		<title>A*寻路算法(threeJS)</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link rel=stylesheet href="css/base.css" />
		<style>
			#inset {
            width: 200px;
            height: 200px;
            background-color: #fff; /* or transparent; */
            border: 1px solid black; /* or none; */
            margin: 20px;
            padding: 0px;
            position: absolute;
            right: 0px;
            top: 0px;
            z-index: 100;
        }
	</style>

	</head>
	<body>
		<!-- https://me.csdn.net/weixin_39028949  作者思路网址-->
		<script src="js/three.js"></script>
		<script src="js/Detector.js"></script>
		<script src="js/Stats.js"></script>
		<script src="js/OrbitControls.js"></script>
		<script src="js/THREEx.KeyboardState.js"></script>
		<script src="js/THREEx.FullScreen.js"></script>
		<script src="js/THREEx.WindowResize.js"></script>
		<script type="text/javascript" src="js/astar.js"></script>
		<script src="js/jquery-1.9.1.js"></script>
		<script src="js/jquery-ui.js"></script>
		<script type="text/javascript" src="js/CanvasRenderer.js"></script>
		<script type="text/javascript" src="js/Projector.js"></script>
		<link rel=stylesheet href="css/jquery-ui.css" />

		<div id="ThreeJS" style="position: absolute; left:0px; top:0px"></div>
		<div id="inset"></div>
		<script>
			// MAIN

			// standard global variables
			var container, scene, camera, renderer, controls, stats, container2, scene2, camera2, renderer2;
			//var keyboard = new THREEx.KeyboardState();

			// custom global variables
			var length = 200;
			var flex = 2;

			var track, curve, texture; //路线对象
			var progress = 0;
			init();
			//render();
			animate();
			// FUNCTIONS 		
			function init() {
				// SCENE
				scene = new THREE.Scene();
				var graph = [];
				// CAMERA
				var SCREEN_WIDTH = window.innerWidth,
					SCREEN_HEIGHT = window.innerHeight;
				var VIEW_ANGLE = 45,
					ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT,
					NEAR = 0.1,
					FAR = 20000;
				var raycaster;
				var CANVAS_WIDTH = 200;
				var CANVAS_HEIGHT = 200;
				camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
				scene.add(camera);
				camera.position.set(0, 200, 250);
				camera.lookAt(scene.position);
				// RENDERER
				if (Detector.webgl)
					renderer = new THREE.WebGLRenderer({
						antialias: true
					});
				else
					renderer = new THREE.CanvasRenderer();
				renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
				container = document.getElementById('ThreeJS');
				container.appendChild(renderer.domElement);
				// EVENTS
				THREEx.WindowResize(renderer, camera);
				THREEx.FullScreen.bindKey({
					charCode: 'm'.charCodeAt(0)
				});
				// CONTROLS
				controls = new THREE.OrbitControls(camera, renderer.domElement);
				// STATS
				// dom

				stats = new Stats();
				stats.domElement.style.position = 'absolute';
				stats.domElement.style.bottom = '0px';
				stats.domElement.style.zIndex = 100;
				container.appendChild(stats.domElement);
				// LIGHT
				var light = new THREE.PointLight(0xffffff);
				light.position.set(-100, 200, 100);
				scene.add(light);
				var ambientLight = new THREE.AmbientLight(0x111111);
				scene.add(ambientLight);
				initGround();

				function initGround() {
					// lines
					var geometry = new THREE.Geometry();
					geometry.vertices.push(new THREE.Vector3(-length / 2, 0, 0));
					geometry.vertices.push(new THREE.Vector3(length / 2, 0, 0));

					for (var i = 0; i <= length / 10; i++) {

						var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
							color: 0x808080,
							opacity: 1
						}));
						line.position.z = (i * 10) - length / 2;
						scene.add(line);

						var line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
							color: 0x808080,
							opacity: 1
						}));
						line.position.x = (i * 10) - length / 2;
						line.rotation.y = 90 * Math.PI / 180;
						scene.add(line);

					}
					var geometry = new THREE.PlaneGeometry(length, 10);
					var material = new THREE.MeshBasicMaterial({
						color: 0x808080,
						side: THREE.DoubleSide
					});
					var plane = new THREE.Mesh(geometry, material);
					plane.position.set(0, 5, length / 2);
					scene.add(plane);
					var plane = new THREE.Mesh(geometry, material);
					plane.rotation.y = 90 * Math.PI / 180;
					plane.position.set(length / 2, 5, 0);
					scene.add(plane);
					var plane = new THREE.Mesh(geometry, material);
					plane.position.set(0, 5, -length / 2);
					scene.add(plane);
					var plane = new THREE.Mesh(geometry, material);
					plane.rotation.y = 90 * Math.PI / 180;
					plane.position.set(-length / 2, 5, 0);
					scene.add(plane);

					var skyBoxGeometry = new THREE.CubeGeometry(10000, 10000, 10000);
					var skyBoxMaterial = new THREE.MeshBasicMaterial({
						color: 0xFFFFFF,
						side: THREE.BackSide
					});
					var skyBox = new THREE.Mesh(skyBoxGeometry, skyBoxMaterial);
					scene.add(skyBox);

					scene.fog = new THREE.FogExp2(0xFFFFFF, 0.00025);

					createjiantou();

				}
				initGrid();

				function initGrid() {
					var geometry = new THREE.CubeGeometry(8, 8, 8);
					var material = new THREE.MeshBasicMaterial({
						color: 0xC0C0C0
					});
					for (var i = 0; i < length / 10; i++) {
						var nodeRow = [];
						graph.push(nodeRow);
						for (var j = 0; j < length / 10; j++) {
							var salt = randomNum(1, 7);
							if (salt > flex) {
								nodeRow.push(1);
							} else {
								nodeRow.push(0);
							}
							if (salt <= flex) {
								var cube = new THREE.Mesh(geometry, material);
								cube.position.set(10 * j - length / 2 + 5, 5, 10 * i - length / 2 + 5);
								scene.add(cube);
							}
						}
					}
					console.log(graph);
				}

				//	container2 = document.getElementById('inset');
				//  // renderer
				//  renderer2 = new THREE.CanvasRenderer();
				//  renderer2.setSize(CANVAS_WIDTH, CANVAS_HEIGHT);
				//  container2.appendChild(renderer2.domElement);
				//  // scene
				//  scene2 = new THREE.Scene();
				//  // camera
				//  camera2 = new THREE.PerspectiveCamera(90, CANVAS_WIDTH / CANVAS_HEIGHT, 1, 1000);
				//  camera2.position.set(0,200,0)
				//  camera2.up=camera.up;
				//  camera2.lookAt(0,1,10);
				//  scene2.add(camera2);
				//  var dir = new THREE.Vector3( 20, 20, 20 );
				//  var origin = new THREE.Vector3( 0, 0, 0 );
				//  var length = 50;
				//  var hex = 0x000000;
				//  var arrowHelper = new THREE.ArrowHelper( dir.normalize(), origin, length, hex );
				//  scene2.add( arrowHelper );
				//
				//  // axes
				//  axes2 = new THREE.AxisHelper(200);
				//  scene2.add(axes2);


				var reslutArray = new Array();
				raycaster = new THREE.Raycaster();
				let mouse = new THREE.Vector2();

				function pickupObjects(e) {
					//将鼠标点击位置的屏幕坐标转成threejs中的标准坐标
					var fxl = new THREE.Vector3(0, 1, 0);
					var groundplane = new THREE.Plane(fxl, 0);
					mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
					mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
					//从相机发射一条射线，经过鼠标点击位置
					raycaster.setFromCamera(mouse, camera);
					//计算射线相机到的对象，可能有多个对象，因此返回的是一个数组，按离相机远近排列
					var ray = raycaster.ray;
					let intersects = ray.intersectPlane(groundplane);
					let x = intersects.x;
					let z = intersects.z;
					//console.log(x+" "+z);
					if (Math.abs(x) > length / 2 || Math.abs(z) > (length / 2)) {
						return;
					}
					/* 
					if((Math.abs(x)<=((length-20)/2)&&Math.abs(z)<=(length-20)/2)){
						alert("请在边缘位置选择开始或结束点");
						return;
					} 
					*/
					var k, m;
					for (var i = -length / 2; i < length / 2; i += 10) {
						if (x >= i && x < i + 10) {
							k = i + 5;
						}
					}
					for (var j = -length / 2; j < length / 2; j += 10) {
						if (z >= j && z < j + 10) {
							m = j + 5;
						}
					}
					console.log(k + ' ' + m);
					initSphere(k, m);

				}
				document.addEventListener('click', pickupObjects, false);
				var isCaculate = false;

				function cleanSphere() {
					let child = scene.children;
					for (var i = 0; i < child.length; i++) {
						if (child[i].geometry instanceof THREE.SphereGeometry) {
							scene.remove(child[i]);
							i--;
						}
					}
					isCaculate = false;
				}

				function initSphere(x, z) {
					if (isCaculate) {
						cleanSphere();
					}
					if (reslutArray.length == 0) {
						//清除路径
						console.log('清除路径');
						scene.remove(track);
						scene.remove(circleP);
						circleP.position.set(x, 0, z);
						scene.add(circleP);
						progress = 0;
						var geometry = new THREE.SphereGeometry(5, 32, 32);
						var material = new THREE.MeshBasicMaterial({
							color: 0xffff00
						});
						var sphere = new THREE.Mesh(geometry, material);
						sphere.position.x = x;
						sphere.position.y = 5;
						sphere.position.z = z;
						sphere.name = "sphere";
						reslutArray.push(sphere);
						scene.add(sphere);
					} else if (reslutArray[0].position.x != x && reslutArray[0].position.z != z) {
						var geometry = new THREE.SphereGeometry(5, 32, 32);
						var material = new THREE.MeshBasicMaterial({
							color: 0xffff00
						});
						var sphere = new THREE.Mesh(geometry, material);
						sphere.position.x = x;
						sphere.position.y = 5;
						sphere.position.z = z;
						sphere.name = "sphere1";
						reslutArray.push(sphere);
						scene.add(sphere);
						caculatePath(reslutArray);
						isCaculate = true;
						reslutArray = new Array();
					}
					//console.log(reslutArray);

				}

				function caculatePath(resultArray) {
					//console.log(resultArray);
					var maps = new Graph(graph);
					var startX = (resultArray[0].position.z - 5 + length / 2) / 10;
					var startY = (resultArray[0].position.x - 5 + length / 2) / 10;
					var endX = (resultArray[1].position.z - 5 + length / 2) / 10;
					var endY = (resultArray[1].position.x - 5 + length / 2) / 10;
					//console.log(startX+' '+startY+' '+endX+' '+endY);
					var start = maps.grid[startX][startY];
					var end = maps.grid[endX][endY];
					var result = astar.search(maps, start, end);
					if (result.length == 0) {
						alert("无可到达路径");
						cleanSphere();
						return;
					}
					console.log(result);

					var arr = [];

					for (var i in result) {
						arr.push(new THREE.Vector3(result[i].y * 10 - 100, 0, result[i].x * 10 - 100));
					}


					/**
					 * 创建一个设置重复纹理的管道
					 */

					curve = new THREE.CatmullRomCurve3(arr, false /*是否闭合*/ );
					console.log(arr);

					/*
					var tubeGeometry = new THREE.TubeGeometry(curve, 100, 0.6, 50, false);
		
		
					var textureLoader = new THREE.TextureLoader();
					texture = textureLoader.load('./img/run.png');
					texture.wrapS = THREE.RepeatWrapping
					texture.wrapT= THREE.RepeatWrapping
					texture.repeat.x = 20;
					var tubeMaterial = new THREE.MeshPhongMaterial({
					  map: texture,
					  transparent: true,
					});
					var tube = new THREE.Mesh(tubeGeometry, tubeMaterial);
					scene.add(tube)
					*/

					/**
					 * 创建一个半透明管道  
					 */

					var textureLoader = new THREE.TextureLoader();
					texture = textureLoader.load('./img/run.jpg');
					texture.wrapS = THREE.RepeatWrapping
					texture.wrapT = THREE.RepeatWrapping
					texture.repeat.x = 20;

					var tubeGeometry2 = new THREE.TubeGeometry(curve, 100, 2, 50, false);
					var tubeMaterial2 = new THREE.MeshPhongMaterial({
						map: texture,
						transparent: true,
						opacity: 1,
					});
					track = new THREE.Mesh(tubeGeometry2, tubeMaterial2);
					scene.add(track);

					/*
					//小人box
					//geometryP = new THREE.CircleGeometry( 5, 32 );               
					geometryP = new THREE.SphereGeometry(5,16,16); 
					var materialP = new THREE.MeshBasicMaterial( { color: 0xff0000 ,side:THREE.DoubleSide} );
					circleP = new THREE.Mesh( geometryP, materialP );
					scene.add( circleP );
					geometryP.rotateY(Math.PI/2);
					console.log(352,startX, startY);
					circleP.position.set(startY, 0, startX);
					console.log(circleP);
					*/



					/*
	    var timesRun = 0;
		var interval = setInterval(function(){
			
			var geometry = new THREE.SphereGeometry( 5, 32, 32 );
			var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
			var sphere = new THREE.Mesh( geometry, material );
			sphere.position.x=result[timesRun].y*10-length/2+5;
			sphere.position.y=5;
			sphere.position.z=result[timesRun].x*10-length/2+5;
			scene.add( sphere );
			timesRun += 1;
			if(timesRun == result.length){clearInterval(interval);}			
			}, 0);
	    */

				}

				function createjiantou() {
					geometryP = new THREE.SphereGeometry(5, 16, 16);
					var materialP = new THREE.MeshBasicMaterial({
						color: 0xff0000,
						side: THREE.DoubleSide
					});
					circleP = new THREE.Mesh(geometryP, materialP);
					//scene.add( circleP );
					//geometryP.rotateY(Math.PI/2);
					//console.log(352,startX, startY);
					//circleP.position.set(startY, 0, startX);
					console.log(circleP);
				}

				function randomNum(minNum, maxNum) {
					switch (arguments.length) {
						case 1:
							return parseInt(Math.random() * minNum + 1, 10);
							break;
						case 2:
							return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
							break;
						default:
							return 0;
							break;
					}
				}


			}

			function animate() {
				console.log('render');
				requestAnimationFrame(animate);
				renderer.render(scene, camera);
				//	renderer2.render(scene, camera2);
				//	camera2.position.set(0,200,0)
				//  camera2.up=camera.up;
				//  camera2.lookAt(0,1,0);
				//camera2.position.copy( camera.position );
				//  camera2.position.setLength( 300 );
				//camera2.lookAt( scene2.position );
				controls.update();
				stats.update();
				if (texture) {
					texture.offset.x -= 0.02
					if (progress > 1.0) {
						return; //停留在管道末端,否则会一直跑到起点 循环再跑
					}
					progress += 0.002;
					console.log(progress);
					if (curve) {
						let point = curve.getPoint(progress);
						if (point && point.x) {
							circleP.position.set(point.x, point.y, point.z);
						}
					}
				}
			}

			/***
			先全局定义有些基础模型，然后在进行计算走路
			*/
		</script>

	</body>
</html>
